package main;

import model.*;
import org.apache.log4j.Logger;
import util.Pair;
import gui.watcher.Watcher;

import java.util.Random;

/**
 * @author Alex Vikharev alex.vikharev@gmail.com
 *         created 25.01.12
 */
public class Game {
    private static final Logger log = Logger.getLogger(Game.class);

    private final Player firstPlayer;
    private final Player secondPlayer;
    private final GameType gameType;
    private Watcher watcher;
    private Field currentField;
    private Figure currentFigure;
    private GameRecord.GameRecordBuilder gameRecordBuilder;
    private GameState state = GameState.BEFORE_START;

    public void setWatcher(Watcher watcher) {
        this.watcher = watcher;
    }

    public GameRecord getGameRecord() {
        return gameRecordBuilder.build();
    }

    public Game(Player firstPlayer, Player secondPlayer, GameType gameType) {
        this.firstPlayer = firstPlayer;
        this.secondPlayer = secondPlayer;
        this.gameType = gameType;
        gameRecordBuilder = GameRecord.builder(gameType);
    }

    public void go() {
        Random random = new Random();
        state = GameState.FIRST_PLAYER_MOVE;

        currentFigure = Figure.byCode(random.nextInt(16));
        currentField = Field.builder().build();
        int move = 0;
        while (!state.isGameFinished()) {
            log.debug("move: " + ++move);
            switch (state) {
                case FIRST_PLAYER_MOVE:
                    playerMove(firstPlayer);
                    break;
                case SECOND_PLAYER_MOVE:
                    playerMove(secondPlayer);
                    break;
            }
            log.debug("new state: " + state);
        }
    }

    private void playerMove(Player player) {
        Position result = player.setFigure(currentField, currentFigure);

        if (state == GameState.FIRST_PLAYER_MOVE) {
            gameRecordBuilder.firstPlayerMoves(result);
        } else {
            gameRecordBuilder.secondPlayerMoves(result);
        }


        if (log.isDebugEnabled()) {
            Pair<Integer, Integer> diff = Field.getFirstDiffPos(result.getField(), currentField);
            log.debug("Player " + player + " placed figure " + currentFigure + "(" + Integer.toBinaryString(currentFigure.code)
                    + ") on pos " + diff + " and choose figure " + result.getFigure());
        }


        if (!Position.isMoveValid(currentField, currentFigure, result.getField(), result.getFigure(), gameType)) {
            throw new RuntimeException("Player made invalid move");
        } else {
            currentField = result.getField();
            if (currentField.isGameOver(gameType)) {
                state = (player == firstPlayer) ? GameState.FIRST_PLAYER_WIN : GameState.SECOND_PLAYER_WIN;

            } else {
                if (currentField.getUsedFigures().size() < 16) {
                    state = (state == GameState.FIRST_PLAYER_MOVE) ? GameState.SECOND_PLAYER_MOVE : GameState.FIRST_PLAYER_MOVE;
                } else {
                    state = GameState.TIE;
                }
            }
            currentFigure = result.getFigure();
        }
        if (watcher != null) {
            watcher.watch(result, state);
        }
    }

    public Field getCurrentField() {
        return currentField;
    }

    public GameState getState() {
        return state;
    }
}

